package pgSlick

/**
 * Created by yz on 11/5/2022
 */

import com.github.tminglei.slickpg.ExPostgresProfile
import com.github.tminglei.slickpg.json.PgJsonExtensions
import com.github.tminglei.slickpg.utils.JsonUtils.clean
import com.github.tminglei.slickpg.utils.PgCommonJdbcTypes
import com.github.tminglei.slickpg.utils.PlainSQLUtils.{mkGetResult, mkOptionSetParameter, mkSetParameter}
import slick.jdbc.{JdbcType, PositionedResult, PostgresProfile}
import upickle.default._

import scala.reflect.classTag

trait PgUJsonSupport extends PgJsonExtensions with PgCommonJdbcTypes {
  driver: PostgresProfile =>

  import driver.api._

  ///---
  def pgjson: String
  ///---

  trait UJsonCodeGenSupport {
    // register types to let `ExModelBuilder` find them
    if (driver.isInstanceOf[ExPostgresProfile]) {
      driver.asInstanceOf[ExPostgresProfile].bindPgTypeToScala("json", classTag[ujson.Value])
      driver.asInstanceOf[ExPostgresProfile].bindPgTypeToScala("jsonb", classTag[ujson.Value])
    }
  }

  /// alias
  trait JsonImplicits extends UJsonImplicits

  trait UJsonImplicits extends UJsonCodeGenSupport {
    implicit val uJsonTypeMapper: JdbcType[ujson.Value] =
      new GenericJdbcType[ujson.Value](
        pgjson,
        (v) => read[ujson.Value](v),
        (v) => clean(write(v)),
        hasLiteralForm = false
      )

    implicit def uJsonColumnExtensionMethods(c: Rep[ujson.Value]) = {
      new JsonColumnExtensionMethods[ujson.Value, ujson.Value](c)
    }

    implicit def uJsonOptionColumnExtensionMethods(c: Rep[Option[ujson.Value]]) = {
      new JsonColumnExtensionMethods[ujson.Value, Option[ujson.Value]](c)
    }
  }

  trait UJsonPlainImplicits extends UJsonCodeGenSupport {

    implicit class PgJsonPositionedResult(r: PositionedResult) {
      def nextJson() = nextJsonOption().getOrElse(ujson.Null)

      def nextJsonOption() = r.nextStringOption().map(x => read[ujson.Value](x))
    }

    ////////////////////////////////////////////////////////////
    implicit val getJson = mkGetResult(_.nextJson())
    implicit val getJsonOption = mkGetResult(_.nextJsonOption())
    implicit val setJson = mkSetParameter[ujson.Value](pgjson, x => x.toString())
    implicit val setJsonOption = mkOptionSetParameter[ujson.Value](pgjson, x => x.toString())
  }
}

